package de.dhbw.horb.inf2013.vierFarbenProblem.model.solver;

import java.awt.Color;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.ColoredVertex;
import de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.Graph;
import de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.NoColorSetException;
import de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.SolverListenerInterface;
import de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.VertexNotSetException;

/**
 * The Class BacktrackingIterativFourColorSolver.
 */
public class BacktrackingIterativFourColorSolver implements FourColorSolver {
	
	/** The colors. */
	private Set<Color> colors = new HashSet<Color>();
	
	/** The graph. */
	private Graph graph;
	
	/** The listeners. */
	private List<SolverListenerInterface> listeners = new LinkedList<>();
	
	/* (non-Javadoc)
	 * @see de.dhbw.horb.inf2013.vierFarbenProblem.model.solver.FourColorSolver#solve()
	 */
	@Override
	public Graph solve() throws VertexNotSetException, NoColorSetException {
		int currentVertexId = 1;
		Color lastColor = colors.toArray(new Color[1])[colors.size()-1];
		
		while(true){
			ColoredVertex currentVertex = graph.getVertex(currentVertexId);
			
			if(!currentVertex.hasColor()){
				currentVertex.setColor(colors.toArray(new Color[1])[0]);
			}else{
				Iterator<Color> iterator = colors.iterator();
				boolean nextColor = false;
				Color currentColor = currentVertex.getColor();
				
				while(iterator.hasNext()){
					Color color = iterator.next();
					
					if(nextColor){
						currentVertex.setColor(color);
						break;
					}
					
					if(currentColor.equals(color)){
						nextColor = true;
					}				
				}
			}
			notifySolverListeners();
			
			if(isFullColoredGraph(graph)){
				return graph;
			}else if(isRegularColoredGraph(graph)){
				++currentVertexId;
			}else if(currentVertex.getColor().equals(lastColor)){
				--currentVertexId;
			}
		}
	}

	/* (non-Javadoc)
	 * @see de.dhbw.horb.inf2013.vierFarbenProblem.model.solver.FourColorSolver#addColor(java.awt.Color)
	 */
	@Override
	public void addColor(Color color) {
		this.colors.add(color);
	}
	
	/**
	 * Checks if is full colored graph.
	 *
	 * @param graph the graph
	 * @return true, if is full colored graph
	 * @throws VertexNotSetException the vertex not set exception
	 * @throws NoColorSetException the no color set exception
	 */
	private boolean isFullColoredGraph(Graph graph) throws VertexNotSetException, NoColorSetException{
		boolean allColored = true;
		int id = 1;
		while(graph.containsVertex(id)){
			if(!graph.getVertex(id).hasColor()){
				allColored = false;
			}
			id++;
		}
		return (allColored && isRegularColoredGraph(graph));
	}
	
	/**
	 * Checks if is regular colored graph.
	 *
	 * @param graph the graph
	 * @return true, if is regular colored graph
	 * @throws VertexNotSetException the vertex not set exception
	 * @throws NoColorSetException the no color set exception
	 */
	private boolean isRegularColoredGraph(Graph graph) throws VertexNotSetException, NoColorSetException{
		boolean regularColored = true;
		int id = 1;
		while(graph.containsVertex(id)){
			ColoredVertex currentVertex = graph.getVertex(id);
			if(currentVertex.hasColor()){
				Color color = currentVertex.getColor();
				Integer[] neighbors = currentVertex.getNeighbors();
				
				for(int i = 0; i < neighbors.length; i++){
					if(neighbors[i] != id){
						ColoredVertex neighbor = graph.getVertex(neighbors[i]);
						if(neighbor.hasColor()){
							if(neighbor.getColor().equals(color)){
								regularColored = false;
							}
						}
					}
				}
			}
			id++;
		}
		return regularColored;
	}
	
	/* (non-Javadoc)
	 * @see de.dhbw.horb.inf2013.vierFarbenProblem.model.solver.FourColorSolver#setGraph(de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.Graph)
	 */
	public void setGraph(Graph graph){
		this.graph = graph;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		try {
			solve();
		} catch (VertexNotSetException e) {
			e.printStackTrace();
		} catch (NoColorSetException e) {
			e.printStackTrace();
		}
	}
	
	/* (non-Javadoc)
	 * @see de.dhbw.horb.inf2013.vierFarbenProblem.model.solver.FourColorSolver#addSolverListener(de.dhbw.horb.inf2013.vierFarbenProblem.model.datatypes.SolverListenerInterface)
	 */
	public void addSolverListener(SolverListenerInterface listener){
		this.listeners.add(listener);
	}
	
	/**
	 * Notify solver listeners.
	 */
	public void notifySolverListeners(){
		Iterator<SolverListenerInterface> iterator = listeners.iterator();
		while(iterator.hasNext()){
			iterator.next().update();
		}
	}

	/* (non-Javadoc)
	 * @see de.dhbw.horb.inf2013.vierFarbenProblem.model.solver.FourColorSolver#getGraph()
	 */
	@Override
	public Graph getGraph() {
		return graph;
	}

}
